package com.partsoft.dits.log;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LocationInfo;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.ThrowableInformation;
import org.springframework.util.ClassUtils;

import com.partsoft.dits.EdgeDescriptor;
import com.partsoft.dits.EdgeException;
import com.partsoft.dits.message.MessageException;
import com.partsoft.dits.utils.ExceptionUtils;

/**
 * 适配的后台日志添加器（LOG4J实现）
 */
public class AdaptedDaemonLogAppender extends AbstractDaemonLogAppender {
	
	private LogAdapter logAdapter = null;
	
	@Override
	public synchronized void doAppend(LoggingEvent event) {
		if (logAdapter == null) {
			LogLog.error("未设置日志适配器。");
			return;
		}
		super.doAppend(event);
	}
	
	public LogAdapter getLogAdapter() {
		return logAdapter;
	}

	public synchronized void setLogAdapter(LogAdapter logAdapter) {
		this.logAdapter = logAdapter;
	}

	public void setLayout(Layout layout) {
		setLogAdapter((LogAdapter)layout);
	}
	
	protected LogAttachment[] resolveLogAttachments(LoggingEvent event) {
		LogAttachment []returnArray = new LogAttachment [0];
		if (event.getThrowableInformation() != null) {
			List<LogAttachment> attachs = new LinkedList<LogAttachment>();
			try {
				attachs.add(new BaseLogAttachment(LogAttachment.LOG_EXCEPTION, ExceptionUtils.stackTraceToStringWriter(
						event.getThrowableInformation().getThrowable()).getBuffer().toString().getBytes("utf-8")));
			} catch (UnsupportedEncodingException e) {}
			
			MessageException mex = ExceptionUtils.findCause(event.getThrowableInformation().getThrowable(), MessageException.class);
			if (mex != null) {
				if (mex.getXmlSource() != null) {
					attachs.add(new BaseLogAttachment(LogAttachment.LOG_BYTES,mex.getXmlSource()));
				}
			}
			returnArray = attachs.toArray(returnArray);
		}
		
		return returnArray;
	}
	
	@SuppressWarnings("unchecked")
	private void writeLogEntityEdgeException(BaseLogEntity logEntity, EdgeException ex) {
		if (ex != null && ex.getDescriptor() != null) {
			logEntity.setType(LogType.APPS);
			logEntity.setDescriptorClass((Class<? extends EdgeDescriptor>) ClassUtils.getUserClass(ex.getDescriptor()));
			logEntity.setDescriptorIdentity(ex.getDescriptor().getId());
		}
	}
	
	protected BaseLogEntity convertLoggingEvent(LoggingEvent event) {
		BaseLogEntity logEntity = new BaseLogEntity();
		logEntity.setType(LogType.USER);
		if (event.getLevel().equals(Level.ERROR)) {
			logEntity.setLevel(LogLevel.ERROR);
		} else if (event.getLevel().equals(Level.INFO)) {
			logEntity.setLevel(LogLevel.INFO);
		} else if (event.getLevel().equals(Level.WARN)) {
			logEntity.setLevel(LogLevel.WARN);
		} else {
			logEntity.setLevel(LogLevel.OTHER);
		}
		logEntity.setContent(event.getRenderedMessage());
		logEntity.setLogDate(new Date(event.timeStamp));
		logEntity.setThreadName(event.getThreadName());
		if (event.getLocationInformation() != null) {
			LocationInfo li = event.getLocationInformation();
			logEntity.setFileName(li.getFileName());
			logEntity.setClassName(li.getClassName());
			logEntity.setMethodName(li.getMethodName());
			logEntity.setLineNumber(li.getLineNumber());
		}
		
		if (ClassUtils.isAssignableValue(EdgeException.class, event.getMessage())) {
			EdgeException ex = (EdgeException) event.getMessage();
			writeLogEntityEdgeException(logEntity, ex);
		} else if (event.getThrowableInformation() != null) {
			logEntity.setType(LogType.SYSTEM);
			ThrowableInformation thi = event.getThrowableInformation();
			EdgeException ex = ExceptionUtils.findCause(thi.getThrowable(), EdgeException.class);
			writeLogEntityEdgeException(logEntity, ex);
		}
		
		List<LogAttachment> atts = new LinkedList<LogAttachment>();
		if (logEntity.getContent() != null && logEntity.getContent().length() > LogAdapter.MAX_CONTENT_SIZE) {
			String sc = logEntity.getContent();
			logEntity.setContent(sc.substring(0, LogAdapter.MAX_CONTENT_SIZE - 3) + "...");
			try {
				atts.add(new BaseLogAttachment(LogAttachment.LOG_MESSAGE, sc.getBytes("utf-8")));
			} catch (Exception e) {}
		}
		if (logEntity.getFileName() != null && logEntity.getFileName().length() > LogAdapter.MAX_FILENAME_SIZE) {
			logEntity.setFileName("..." + logEntity.getFileName().substring(logEntity.getFileName().length() - LogAdapter.MAX_FILENAME_SIZE + 3));
		}
		if (logEntity.getClassName() != null && logEntity.getClassName().length() > LogAdapter.MAX_CLASSNAME_SIZE) {
			logEntity.setClassName("..." + logEntity.getClassName().substring(logEntity.getClassName().length() - LogAdapter.MAX_CLASSNAME_SIZE + 3));
		}
		if (logEntity.getMethodName() != null && logEntity.getMethodName().length() > LogAdapter.MAX_METHODNAME_SIZE) {
			logEntity.setMethodName(logEntity.getMethodName().substring(0, LogAdapter.MAX_METHODNAME_SIZE - 3) + "...");
		}
		atts.addAll(Arrays.asList(resolveLogAttachments(event)));
		logEntity.setAttachments(atts);
		return logEntity;
	}

	@Override
	protected void append(LoggingEvent event) {
		logAdapter.savelog(convertLoggingEvent(event));
	}
	
}
